package com.pwr.app.ui;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.sql.Savepoint;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.swing.AbstractButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;

import com.pwr.app.BiPolarUtil;
import com.pwr.app.Noise;
import com.pwr.app.Pattern;
import com.pwr.app.TestType;
import com.pwr.app.Constants.Locations;
import com.pwr.app.Constants.NoiseMethods;
import com.pwr.app.characteristics.CharacteristicsEnum;
import com.pwr.app.classification.ClassificationEnum;
import com.pwr.app.classification.NearestNeighbour;
import com.pwr.app.classification.NeuralNet;
import com.pwr.app.ui.WindowHelper.ImageFile;

public class WindowModel implements ActionListener {
	private ButtonPanel buttonPanel;
	private ArrayList<String> imagesPaths;

	public static final int WIDTH = 300;
	public static final int HEIGHT = 300;

	public JFrame frame;
	private JTextField input;
	JPanel classificatorRadioPanel;
	JRadioButton[] radioButtons = new JRadioButton[2];
	String[] radioNames = { "MLP", "kNN" };

	NeuralNet neuratlNet;
	NearestNeighbour nearestNeighbour;
	Map<CharacteristicsEnum, Boolean> chosenCharacteristics;
	TestType chosenTestType = TestType.VectorLenght;
	ClassificationEnum chosenClassificator = ClassificationEnum.NeuralNet;

	public WindowModel() {
		// Create the neural network.
		buttonPanel = new ButtonPanel(this);
		neuratlNet = new NeuralNet();
		nearestNeighbour = new NearestNeighbour();
		chosenCharacteristics = new HashMap<CharacteristicsEnum, Boolean>();
		chosenCharacteristics.put(CharacteristicsEnum.Density, false);
		chosenCharacteristics.put(CharacteristicsEnum.Fourier, true);
		chosenCharacteristics.put(CharacteristicsEnum.Histogram, false);
		chosenCharacteristics.put(CharacteristicsEnum.HuMoment, false);
		createAndShowGUI();
	}

	public void prepareRadioButtons(ActionListener al) {
		for (int i = 0; i < radioButtons.length; i++) {
			radioButtons[i] = new JRadioButton();
			if (i == 0)
				radioButtons[i].setSelected(true);
			radioButtons[i].setText(radioNames[i]);
			radioButtons[i].addActionListener(al);
			classificatorRadioPanel.add(radioButtons[i]);
		}
	}

	public JPanel CreateContentPanel(int GRID_SIZE) {
		JPanel TotalGUI = new JPanel(new BorderLayout(10, 10));

		JPanel fieldButtons = new JPanel(new GridLayout(buttonPanel.getGRID_SIZE(), buttonPanel.getGRID_SIZE()));
		JPanel action_buttons = new JPanel(new GridLayout(buttonPanel.getButtonsListSize(), 1));
		TotalGUI.setPreferredSize(new Dimension(WIDTH, HEIGHT));

		classificatorRadioPanel = new JPanel();
		prepareRadioButtons(this);

		input = new JTextField("Current network size: " + buttonPanel.getGRID_SIZE());
		input.setEditable(false);
		input.setHorizontalAlignment(JTextField.CENTER);

		for (int i = 0; i < buttonPanel.getGRID_SIZE(); i++)
			for (int j = 0; j < buttonPanel.getGRID_SIZE(); j++)
				fieldButtons.add(buttonPanel.getPixelButton((buttonPanel.getGRID_SIZE() * i) + j));

		for (int i = 0; i < buttonPanel.getButtonsList().size(); i++)
			action_buttons.add(buttonPanel.getButton(i));

		TotalGUI.add(input, BorderLayout.NORTH);
		TotalGUI.add(classificatorRadioPanel, BorderLayout.SOUTH);
		TotalGUI.add(fieldButtons, BorderLayout.CENTER);
		TotalGUI.add(action_buttons, BorderLayout.WEST);

		buttonPanel.ifDefine = false;
		return TotalGUI;
	}

	private void createAndShowGUI() {
		JFrame.setDefaultLookAndFeelDecorated(true);
		frame = new JFrame("Character Recognition");
		Toolkit tk = frame.getToolkit();
		Image icon = tk.getImage("icon.png"); // load application icon
		frame.setIconImage(icon);
		// Set the content pane.
		frame.setContentPane(CreateContentPanel(20));
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.pack();
		Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
		frame.setLocation(dim.width / 2 - frame.getWidth() / 2, dim.height / 2 - frame.getHeight() / 2);
		frame.setVisible(true);
	}

	private void refreshGUI() {
		// Resize the neural network.
		buttonPanel.refreshButtonPanel(this);
		frame.setContentPane(CreateContentPanel(buttonPanel.getGRID_SIZE()));
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
		switch (chosenClassificator) {
		case NearestNeighbour:
			((AbstractButton) classificatorRadioPanel.getComponent(0)).setSelected(false);
			((AbstractButton) classificatorRadioPanel.getComponent(1)).setSelected(true);
			break;
		case NeuralNet:
			((AbstractButton) classificatorRadioPanel.getComponent(1)).setSelected(false);
			((AbstractButton) classificatorRadioPanel.getComponent(0)).setSelected(true);
			break;
		}
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		Object source = e.getSource();
		String buttonName = ((AbstractButton) source).getActionCommand();

		for (int i = 0; i < buttonPanel.getGRID_SIZE() * buttonPanel.getGRID_SIZE(); i++)
			if (source == buttonPanel.getPixelButton(i)) {
				buttonPanel.colorPixelButton(i);
				break;
			}

		if (buttonName == "TRAIN") {
			imagesPaths = new ArrayList<String>();

			ArrayList<ImageFile> images = WindowHelper.loadImage(frame, imagesPaths, "Choose images for learning process:", true);
			if (images.size() != 0) {
				if (WindowHelper.checkImagesSize(images)) {
					new LearningWindow(frame, "Characterisitics", "", chosenCharacteristics);
					ArrayList<Pattern> arrayedPattern = WindowHelper.loadImagesIntoPatternList(images, chosenCharacteristics, new int[] { 25, 10, 25, 7 });

					nearestNeighbour.teach(arrayedPattern);
					neuratlNet.teach(arrayedPattern);
				}
			}
			buttonPanel.setPanelMessage("Learning process completed");
			input.setText(buttonPanel.getPanelMessage());

		} else if (buttonName == "MLP") {
			chosenClassificator = ClassificationEnum.NeuralNet;
			((AbstractButton) classificatorRadioPanel.getComponent(1)).setSelected(false);
			((AbstractButton) classificatorRadioPanel.getComponent(0)).setSelected(true);

		} else if (buttonName == "kNN") {
			chosenClassificator = ClassificationEnum.NearestNeighbour;
			((AbstractButton) classificatorRadioPanel.getComponent(0)).setSelected(false);
			((AbstractButton) classificatorRadioPanel.getComponent(1)).setSelected(true);
		} else if (buttonName == "RECOGNIZE") {
			double[] pattern = WindowHelper.processCharacteristics(BiPolarUtil.double2bipolar(buttonPanel.getPixelsArray()), chosenCharacteristics, new int[] { 25, 10, 25, 7 });
			int letter = -1;
			switch (chosenClassificator) {
			case NearestNeighbour:
				letter = WindowHelper.processRecognition(pattern, nearestNeighbour);
				break;
			case NeuralNet:
				letter = WindowHelper.processRecognition(pattern, neuratlNet);
				break;
			}
			buttonPanel.setPanelMessage("Letter recognized: " + (char) letter);
			// buttonPanel.setPixelsArray(BiPolarUtil.bipolar_2_2ddouble(result));
			buttonPanel.ifDefine = false;
			refreshGUI();
			input.setText(buttonPanel.getPanelMessage());
		} else if (buttonName == "LOAD IMAGE") {
			ArrayList<ImageFile> images = WindowHelper.loadImage(frame, imagesPaths, "Choose an image for recognition process:", false);
			if (images != null && images.size() == 1) {
				buttonPanel.loadImageIntoPixelsArray(images.get(0).data);
				buttonPanel.setPanelMessage("Image loaded");
				refreshGUI();
				input.setText(buttonPanel.getPanelMessage());
			} else if (images != null) {
				JOptionPane.showMessageDialog(frame, "Loaded too many images!!! (Select only one you want to read)");
			}
		} else if (buttonName == "NOISE") {
			NoiseWindow noiseWindow = new NoiseWindow(frame, "Noise selection", "");
			double[][] pixelsArray = buttonPanel.getPixelsArray();
			switch (noiseWindow.method) {
			case SALT_AND_PEPPER: // GAUSS
				pixelsArray = Noise.imageSaltAndPeper(pixelsArray, noiseWindow.percentage);
				buttonPanel.setPixelsArray(pixelsArray);
				refreshGUI();
				input.setText("Image processed with Gauss Noise");
				break;
			case HOVER:
				pixelsArray = Noise.imageHover(pixelsArray, noiseWindow.percentage, noiseWindow.location);
				buttonPanel.setPixelsArray(pixelsArray);
				refreshGUI();
				input.setText("Image processed with Hover");
				break;
			case SHEAR:
				pixelsArray = Noise.imageShear(pixelsArray, noiseWindow.percentage);
				buttonPanel.setPixelsArray(pixelsArray);
				refreshGUI();
				input.setText("Image processed with Shear");
				break;
			case ROTATION:
				pixelsArray = Noise.imageRotation(pixelsArray, noiseWindow.percentage);
				buttonPanel.setPixelsArray(pixelsArray);
				refreshGUI();
				input.setText("Image processed with Rotation");
				break;
			default:
				break;
			}

		} else if (buttonName == "TEST") {
			imagesPaths = new ArrayList<String>();
			TestingWindow window = new TestingWindow(frame, "Tests", "", chosenTestType, chosenCharacteristics, chosenClassificator);
			chosenTestType = window.getTestType();
			chosenCharacteristics = window.getCharacteristicsUsed();
			chosenClassificator = window.getChosenClassificator();
			ArrayList<ImageFile> images = WindowHelper.loadImage(frame, imagesPaths, "Choose images for learning process:", true);
			imagesPaths = new ArrayList<String>();

			if (images.size() != 0) {
				WindowHelper.loadImage(frame, imagesPaths, "Choose images for testing", true);
				if (WindowHelper.checkImagesSize(images)) {
					switch (chosenTestType) {
					case VectorLenght:
						for (Iterator<Entry<CharacteristicsEnum, Boolean>> i = chosenCharacteristics.entrySet().iterator(); i.hasNext();) {
							HashMap<CharacteristicsEnum, Boolean> chosenChar = new HashMap<CharacteristicsEnum, Boolean>();
							chosenChar.put(CharacteristicsEnum.Density, false);
							chosenChar.put(CharacteristicsEnum.Fourier, false);
							chosenChar.put(CharacteristicsEnum.Histogram, false);
							chosenChar.put(CharacteristicsEnum.HuMoment, false);

							Entry<CharacteristicsEnum, Boolean> item = i.next();
							if (item.getValue()) {
								chosenChar.put(item.getKey(), true);
								if (chosenClassificator == ClassificationEnum.NearestNeighbour) {
									WindowHelper.testNetworkCount(images, nearestNeighbour, chosenChar, imagesPaths);
								} else {
									WindowHelper.testNetworkCount(images, neuratlNet, chosenChar, imagesPaths);
								}
							}
						}
						break;
					case Effectivness:
						int[] charCount = null;
						if (chosenClassificator == ClassificationEnum.NearestNeighbour) {
							charCount = new int[] { 10, 15, 22, 7 };
							ArrayList<Pattern> arrayedPattern = WindowHelper.loadImagesIntoPatternList(images, chosenCharacteristics, charCount);
							nearestNeighbour.teach(arrayedPattern);
							WindowHelper.testNetwork(imagesPaths, nearestNeighbour, chosenCharacteristics, charCount);
						} else {
							charCount = new int[] { 22, 13, 17, 7 };
							ArrayList<Pattern> arrayedPattern = WindowHelper.loadImagesIntoPatternList(images, chosenCharacteristics, charCount);
							neuratlNet.teach(arrayedPattern);
							WindowHelper.testNetwork(imagesPaths, neuratlNet, chosenCharacteristics, charCount);
						}

						break;
					}
				}
			}

			// refreshGUI();
			buttonPanel.setPanelMessage("Testing completed!");
			input.setText(buttonPanel.getPanelMessage());
		} else if (buttonName == "CLEAR")
		// CLEAR
		{
			buttonPanel.ifDefine = true;
			refreshGUI();
		}
	}
}
